"""
统一认证管理系统 - 重构版本
==========================

重构后的认证系统，实现AbstractAuthService接口，
使用依赖注入模式，避免循环导入。

特性：
- 实现AbstractAuthService接口
- 支持依赖注入
- 统一的JWT令牌验证
- 多服务用户信息获取
- 会话管理和缓存
- 权限和角色验证
- 安全的令牌刷新机制
"""

from datetime import datetime, timedelta
from jose import JWTError, jwt
from typing import Dict, Any, Optional, List, Union
import json
import logging
import hashlib
import secrets
import uuid

from fastapi import HTTPException, status

from ...interfaces.auth import AbstractAuthService, AbstractTokenConfig, AbstractPasswordManager
from ...interfaces.cache import AbstractCacheManager
from ...interfaces.database import AbstractDatabaseManager
from ...common.config import get_service_config

logger = logging.getLogger(__name__)


class TokenConfig(AbstractTokenConfig):
    """令牌配置实现"""
    
    def __init__(self, config_dict: Dict[str, Any]):
        self._config = config_dict
    
    @property
    def secret_key(self) -> str:
        return self._config.get("secret_key", "default-secret-key")
    
    @property
    def algorithm(self) -> str:
        return self._config.get("algorithm", "HS256")
    
    @property
    def access_token_expire_minutes(self) -> int:
        return self._config.get("access_token_expire_minutes", 30)
        
    @property 
    def refresh_token_expire_days(self) -> int:
        return self._config.get("refresh_token_expire_days", 7)


class PasswordManager(AbstractPasswordManager):
    """密码管理器实现"""
    
    def __init__(self):
        pass
    
    async def hash_password(self, password: str) -> str:
        """哈希密码"""
        import bcrypt
        salt = bcrypt.gensalt()
        hashed = bcrypt.hashpw(password.encode('utf-8'), salt)
        return hashed.decode('utf-8')
    
    async def verify_password(self, password: str, hashed: str) -> bool:
        """验证密码"""
        try:
            import bcrypt
            return bcrypt.checkpw(password.encode('utf-8'), hashed.encode('utf-8'))
        except Exception as e:
            logger.error(f"密码验证失败: {e}")
            return False
    
    async def generate_password(self, length: int = 12) -> str:
        """生成随机密码"""
        import string
        characters = string.ascii_letters + string.digits + "!@#$%^&*"
        return ''.join(secrets.choice(characters) for _ in range(length))
    
    async def validate_password_strength(self, password: str) -> Dict[str, Any]:
        """验证密码强度"""
        import re
        
        strength = {
            "valid": True,
            "score": 0,
            "issues": []
        }
        
        # 长度检查
        if len(password) < 8:
            strength["issues"].append("密码长度至少8位")
            strength["valid"] = False
        else:
            strength["score"] += 1
        
        # 包含大写字母
        if not re.search(r'[A-Z]', password):
            strength["issues"].append("密码应包含大写字母")
        else:
            strength["score"] += 1
        
        # 包含小写字母
        if not re.search(r'[a-z]', password):
            strength["issues"].append("密码应包含小写字母")
        else:
            strength["score"] += 1
        
        # 包含数字
        if not re.search(r'\d', password):
            strength["issues"].append("密码应包含数字")
        else:
            strength["score"] += 1
        
        # 包含特殊字符
        if not re.search(r'[!@#$%^&*(),.?\":{}|<>]', password):
            strength["issues"].append("密码应包含特殊字符")
        else:
            strength["score"] += 1
        
        # 评分
        if strength["score"] >= 4:
            strength["level"] = "strong"
        elif strength["score"] >= 3:
            strength["level"] = "medium"
        else:
            strength["level"] = "weak"
            strength["valid"] = False
        
        return strength


class UnifiedAuthManager(AbstractAuthService):
    """统一认证管理器 - 重构版本
    
    实现AbstractAuthService接口，提供完整的认证服务。
    """
    
    def __init__(
        self,
        token_config: AbstractTokenConfig,
        cache_manager: AbstractCacheManager,
        database_manager: Optional[AbstractDatabaseManager] = None,
        password_manager: Optional[AbstractPasswordManager] = None
    ):
        """初始化认证管理器
        
        Args:
            token_config: 令牌配置
            cache_manager: 缓存管理器
            database_manager: 数据库管理器（可选）
            password_manager: 密码管理器（可选）
        """
        self.token_config = token_config
        self.cache_manager = cache_manager
        self.database_manager = database_manager
        self.password_manager = password_manager or PasswordManager()
        
        # 缓存键前缀
        self.USER_CACHE_PREFIX = "user:"
        self.SESSION_CACHE_PREFIX = "session:"
        self.TOKEN_BLACKLIST_PREFIX = "blacklist:"
        self.REFRESH_TOKEN_PREFIX = "refresh:"
    
    async def create_token(
        self, 
        user_id: str, 
        user_data: Optional[Dict[str, Any]] = None,
        expires_delta: Optional[timedelta] = None
    ) -> str:
        """创建访问令牌"""
        try:
            # 设置过期时间
            if expires_delta:
                expire = datetime.utcnow() + expires_delta
            else:
                expire = datetime.utcnow() + timedelta(
                    minutes=self.token_config.access_token_expire_minutes
                )
            
            # 构建载荷
            payload = {
                "sub": user_id,
                "exp": expire,
                "iat": datetime.utcnow(),
                "jti": str(uuid.uuid4())  # JWT ID，用于令牌撤销
            }
            
            # 添加用户数据
            if user_data:
                payload.update(user_data)
            
            # 生成令牌
            token = jwt.encode(
                payload, 
                self.token_config.secret_key, 
                algorithm=self.token_config.algorithm
            )
            
            return token
            
        except Exception as e:
            logger.error(f"创建令牌失败: {e}")
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="令牌创建失败"
            )
    
    async def verify_token(self, token: str) -> Optional[Dict[str, Any]]:
        """验证访问令牌"""
        try:
            # 解码令牌
            payload = jwt.decode(
                token,
                self.token_config.secret_key,
                algorithms=[self.token_config.algorithm]
            )
            
            # 检查令牌是否在黑名单中
            jti = payload.get("jti")
            if jti:
                blacklisted = await self.cache_manager.exists(
                    f"{self.TOKEN_BLACKLIST_PREFIX}{jti}"
                )
                if blacklisted:
                    logger.warning(f"令牌已被撤销: {jti}")
                return None
            
            return payload
            
        except JWTError as e:
            logger.warning(f"令牌验证失败: {e}")
            return None
        except Exception as e:
            logger.error(f"令牌验证异常: {e}")
            return None
    
    async def refresh_token(self, refresh_token: str) -> Optional[str]:
        """刷新访问令牌"""
        try:
            # 验证刷新令牌
            user_id = await self.verify_refresh_token(refresh_token)
            if not user_id:
                return None
            
            # 获取用户信息
            user_data = await self._get_user_data(user_id)
            if not user_data:
                logger.warning(f"用户不存在: {user_id}")
                return None
            
            # 创建新的访问令牌
            new_token = await self.create_token(user_id, user_data)
            
            return new_token
            
        except Exception as e:
            logger.error(f"刷新令牌失败: {e}")
            return None
    
    async def revoke_token(self, token: str) -> bool:
        """撤销令牌"""
        try:
            # 解码令牌获取JTI
            payload = jwt.decode(
                token,
                self.token_config.secret_key,
                algorithms=[self.token_config.algorithm],
                options={"verify_exp": False}  # 忽略过期检查
            )
            
            jti = payload.get("jti")
            if not jti:
                logger.warning("令牌没有JTI，无法撤销")
                return False
            
            # 计算剩余有效时间
            exp = payload.get("exp")
            if exp:
                expire_time = datetime.fromtimestamp(exp)
                now = datetime.utcnow()
                if expire_time > now:
                    ttl = int((expire_time - now).total_seconds())
                    # 将令牌加入黑名单
                    await self.cache_manager.set(
                        f"{self.TOKEN_BLACKLIST_PREFIX}{jti}",
                        "revoked",
                        expire=ttl
                    )
            
            return True
            
        except Exception as e:
            logger.error(f"撤销令牌失败: {e}")
            return False
    
    async def create_refresh_token(self, user_id: str) -> str:
        """创建刷新令牌"""
        try:
            # 生成刷新令牌
            refresh_token = secrets.token_urlsafe(32)
            
            # 存储刷新令牌
            expire_seconds = self.token_config.refresh_token_expire_days * 24 * 3600
            await self.cache_manager.set(
                f"{self.REFRESH_TOKEN_PREFIX}{refresh_token}",
                user_id,
                expire=expire_seconds
            )
            
            return refresh_token
            
        except Exception as e:
            logger.error(f"创建刷新令牌失败: {e}")
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="刷新令牌创建失败"
            )
    
    async def verify_refresh_token(self, refresh_token: str) -> Optional[str]:
        """验证刷新令牌"""
        try:
            user_id = await self.cache_manager.get(
                f"{self.REFRESH_TOKEN_PREFIX}{refresh_token}"
            )
            return user_id
            
        except Exception as e:
            logger.error(f"验证刷新令牌失败: {e}")
        return None
    
    async def authenticate_user(self, username: str, password: str) -> Optional[Dict[str, Any]]:
        """用户认证"""
        try:
            if not self.database_manager:
                logger.error("数据库管理器未配置，无法进行用户认证")
                return None
            
            # 查询用户
            user_query = """
                SELECT id, username, email, password_hash, is_active, roles
                FROM users 
                WHERE username = :username OR email = :username
                LIMIT 1
            """
            
            user_record = await self.database_manager.fetch_one(
                user_query, 
                {"username": username}
            )
            
            if not user_record:
                logger.warning(f"用户不存在: {username}")
                return None
            
            # 检查用户是否激活
            if not user_record.get("is_active", True):
                logger.warning(f"用户未激活: {username}")
                return None
            
            # 验证密码
            password_hash = user_record.get("password_hash", "")
            if not await self.password_manager.verify_password(password, password_hash):
                logger.warning(f"密码错误: {username}")
                return None
            
            # 返回用户信息（不包含密码）
            user_data = {
                "user_id": user_record["id"],
                "username": user_record["username"],
                "email": user_record.get("email"),
                "roles": user_record.get("roles", []),
                "is_active": user_record.get("is_active", True)
            }
            
            return user_data
            
        except Exception as e:
            logger.error(f"用户认证失败: {e}")
            return None
    
    async def get_current_user(self, token: str) -> Optional[Dict[str, Any]]:
        """获取当前用户信息"""
        try:
            # 验证令牌
            payload = await self.verify_token(token)
            if not payload:
                return None
            
            user_id = payload.get("sub")
            if not user_id:
                    return None
                    
            # 获取用户信息
            user_data = await self._get_user_data(user_id)
            return user_data
                
        except Exception as e:
            logger.error(f"获取当前用户失败: {e}")
            return None
    
    async def check_permission(
        self, 
        user_id: str, 
        resource: str, 
        action: str
    ) -> bool:
        """检查用户权限"""
        try:
            # 获取用户角色
            roles = await self.get_user_roles(user_id)
            
            # 简单的权限检查逻辑（可以根据需要扩展）
            if "admin" in roles:
                return True
            
            if "super_admin" in roles:
                return True
            
            # 可以在这里添加更复杂的权限检查逻辑
            # 例如基于资源和操作的细粒度权限控制
            
            return False
            
        except Exception as e:
            logger.error(f"权限检查失败: {e}")
            return False
    
    async def get_user_roles(self, user_id: str) -> List[str]:
        """获取用户角色"""
        try:
            user_data = await self._get_user_data(user_id)
            if user_data:
                return user_data.get("roles", [])
            return []
            
        except Exception as e:
            logger.error(f"获取用户角色失败: {e}")
            return []
    
    async def has_role(self, user_id: str, role: str) -> bool:
        """检查用户是否有指定角色"""
        try:
            roles = await self.get_user_roles(user_id)
            return role in roles
            
        except Exception as e:
            logger.error(f"角色检查失败: {e}")
            return False
    
    async def create_session(self, user_id: str, session_data: Optional[Dict[str, Any]] = None) -> str:
        """创建用户会话"""
        try:
            session_id = str(uuid.uuid4())
            
            session_info = {
                "user_id": user_id,
                "created_at": datetime.utcnow().isoformat(),
                "data": session_data or {}
            }
            
            # 会话默认过期时间为24小时
            expire_seconds = 24 * 3600
            await self.cache_manager.set(
                f"{self.SESSION_CACHE_PREFIX}{session_id}",
                session_info,
                expire=expire_seconds
            )
            
            return session_id
            
        except Exception as e:
            logger.error(f"创建会话失败: {e}")
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="会话创建失败"
            )
    
    async def get_session(self, session_id: str) -> Optional[Dict[str, Any]]:
        """获取会话信息"""
        try:
            session_info = await self.cache_manager.get(
                f"{self.SESSION_CACHE_PREFIX}{session_id}"
            )
            return session_info
            
        except Exception as e:
            logger.error(f"获取会话失败: {e}")
        return None
    
    async def destroy_session(self, session_id: str) -> bool:
        """销毁会话"""
        try:
            result = await self.cache_manager.delete(
                f"{self.SESSION_CACHE_PREFIX}{session_id}"
            )
            return result
                
        except Exception as e:
            logger.error(f"销毁会话失败: {e}")
            return False
    
    async def health_check(self) -> Dict[str, Any]:
        """健康检查"""
        try:
            health_info = {
                "healthy": True,
                "message": "认证服务正常",
                "components": {}
            }
            
            # 检查缓存
            cache_health = await self.cache_manager.health_check()
            health_info["components"]["cache"] = cache_health
            
            if not cache_health.get("healthy", False):
                health_info["healthy"] = False
                health_info["message"] = "缓存服务异常"
            
            # 检查数据库（如果配置了）
            if self.database_manager:
                db_health = await self.database_manager.health_check()
                health_info["components"]["database"] = db_health
                
                if not db_health.get("healthy", False):
                    health_info["healthy"] = False
                    health_info["message"] = "数据库服务异常"
            
            return health_info
            
        except Exception as e:
            logger.error(f"认证服务健康检查失败: {e}")
            return {
                "healthy": False,
                "message": f"健康检查异常: {str(e)}",
                "exception": str(e)
            }
    
    async def _get_user_data(self, user_id: str) -> Optional[Dict[str, Any]]:
        """获取用户数据（内部方法）"""
        try:
            # 先从缓存获取
            cache_key = f"{self.USER_CACHE_PREFIX}{user_id}"
            user_data = await self.cache_manager.get(cache_key)
            
            if user_data:
                return user_data
            
            # 如果缓存中没有，从数据库获取
            if self.database_manager:
                user_query = """
                    SELECT id, username, email, roles, is_active, created_at, updated_at
                    FROM users 
                    WHERE id = :user_id
                    LIMIT 1
                """
                
                user_record = await self.database_manager.fetch_one(
                    user_query, 
                    {"user_id": user_id}
                )
                
                if user_record:
                    user_data = {
                        "user_id": user_record["id"],
                        "username": user_record["username"],
                        "email": user_record.get("email"),
                        "roles": user_record.get("roles", []),
                        "is_active": user_record.get("is_active", True),
                        "created_at": user_record.get("created_at"),
                        "updated_at": user_record.get("updated_at")
                    }
                    
                    # 缓存用户数据（15分钟）
                    await self.cache_manager.set(cache_key, user_data, expire=900)
                    
                    return user_data
            
            return None
    
        except Exception as e:
            logger.error(f"获取用户数据失败: {e}")
            return None
    

# 工厂函数
def create_auth_manager(
    service_name: str = "default",
    cache_manager: Optional[AbstractCacheManager] = None,
    database_manager: Optional[AbstractDatabaseManager] = None
) -> UnifiedAuthManager:
    """创建认证管理器实例
    
    Args:
        service_name: 服务名称
        cache_manager: 缓存管理器
        database_manager: 数据库管理器
        
    Returns:
        认证管理器实例
    """
    config = get_service_config(service_name)
    jwt_config = config.get("jwt", {})
    token_config = TokenConfig(jwt_config)
    
    return UnifiedAuthManager(
        token_config=token_config,
        cache_manager=cache_manager,
        database_manager=database_manager
    )


# 导出主要类和函数
__all__ = [
    "TokenConfig",
    "PasswordManager", 
    "UnifiedAuthManager",
    "create_auth_manager"
]
